package com.info.examples.authentication.aop;


import com.info.examples.authentication.AuthToken;
import com.info.examples.authentication.CreditService;
import com.info.examples.authentication.annotation.Authentication;
import com.info.examples.authentication.exception.AuthenticationException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;


/**
 * @desc 使用数字信封方式对输入字段解密，对输出数据加密
 */

@Slf4j
@Aspect
@Component
public class AuthenticationAop {

    private final CreditService creditService;

    public AuthenticationAop(CreditService creditService) {
        this.creditService = creditService;
    }


    @Pointcut("@annotation(com.info.examples.authentication.annotation.Authentication)")
    public void pointCutMethodBefore() {
    }

    @Before("pointCutMethodBefore()")
    public void doBefore(JoinPoint point) {
        MethodInvocationProceedingJoinPoint mjp = (MethodInvocationProceedingJoinPoint) point;
        MethodSignature signature = (MethodSignature) mjp.getSignature();
        Method method = signature.getMethod();
        Authentication annotation = method.getAnnotation(Authentication.class);
        if (annotation != null) {
            HttpServletRequest request = getRequest(point.getArgs());
            if (StringUtils.isEmpty(request)) {
                throw new AuthenticationException("【鉴权失败】，获取HttpServletRequest");
            }
            String appId = request.getParameter("appId");
            if (StringUtils.isEmpty(appId)) {
                throw new AuthenticationException("【鉴权失败】，appId不存在");
            }
            final String secret = creditService.getCreditByAppId(appId);
            final String token = request.getParameter("token");
            final long createTime = Long.parseLong(request.getParameter("createTime"));
            Map<String, String> params = new HashMap<>(1 << 2);
            final String baseurl = request.getRequestURI();
            request.getParameterMap().forEach((k, v) -> params.put(k, v[0]));
            params.remove("token");
            params.put("secret", secret);
            AuthToken authToken = AuthToken.createToken(appId, secret, createTime, baseurl, params);
            log.info(authToken.toString());
            if (!authToken.isExpired()) {
                throw new AuthenticationException("【鉴权失败】，token已过期");
            }
            if (!ObjectUtils.nullSafeEquals(token, authToken.getToken())) {
                throw new AuthenticationException("【鉴权失败】，token错误");
            }
        }
    }


    private HttpServletRequest getRequest(Object[] args) {
        for (Object o : args) {
            if (o instanceof HttpServletRequest) {
                return (HttpServletRequest) o;
            }
        }
        return null;
    }
}
